#include "preHeader.h"
#include "Session/GameSession.h"
#include "Session/GameSessionHandler.h"
#include "SQLHelper.h"

#define SQL_INSERT_MAIL_LENGTH_LIMIT (std::ios::pos_type(1 << 19))

int GameSessionHandler::HandleAcquireMailExpireTime(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto AcquireMailExpireTime = [GSRPCAsyncTaskArgs]() {
		AutoFeedback(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto rst = connPtr->QueryFormat(
			"SELECT MIN(mailExpireTime) FROM `%s`", GetTableName<inst_mail>());
		if (!rst) {
			error = DBPErrorQueryMysqlFailed;
			return;
		}
		if (rst.NextRow()) {
			buffer << rst.Fetch().GetInt64();
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(AcquireMailExpireTime));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleCleanExpireMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto CleanExpireMail = [GSRPCAsyncTaskArgs]() {
		AutoFeedback(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto rst = connPtr->ExecuteFormat(
			"DELETE FROM `%s` WHERE mailExpireTime<%lld",
			GetTableName<inst_mail>(), s64(GET_UNIX_TIME));
		if (!rst.second) {
			error = DBPErrorDeleteMysqlFailed;
			return;
		}
	};
	sDeferAsyncTaskMgr.AddTask(CreateAsyncTask(CleanExpireMail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleGetPlayerMailCount(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto GetPlayerMailCount = [GSRPCAsyncTaskArgs]() {
		auto playerId = pckPtr->Read<uint32>();
		AutoFeedback(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto rst = connPtr->QueryFormat(
			"SELECT COUNT(*) FROM `%s` WHERE mailReceiver=%u",
			GetTableName<inst_mail>(), playerId);
		if (!rst) {
			error = DBPErrorQueryMysqlFailed;
			return;
		}
		rst.NextRow();
		buffer << rst.Fetch().GetUInt32();
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(GetPlayerMailCount));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleGetPlayerMailList(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto GetPlayerMailList = [GSRPCAsyncTaskArgs]() {
		auto playerId = pckPtr->Read<uint32>();
		AutoFeedbackLarge(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto sqlFormat = CreateSQL4SelectEntity<inst_mail>(
			"mailReceiver=%u ORDER BY mailDeliverTime LIMIT 20");
		auto rst = connPtr->QueryFormat(sqlFormat.c_str(), playerId);
		if (!rst) {
			error = DBPErrorQueryMysqlFailed;
			return;
		}
		while (rst.NextRow()) {
			auto row = rst.Fetch();
			auto instInfo = LoadEntityFromMysqlRow<inst_mail>(row);
			SaveToINetStream(instInfo, buffer);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(GetPlayerMailList));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleGetPlayerMailAttachment(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto GetPlayerMailAttachment = [GSRPCAsyncTaskArgs]() {
		auto playerId = pckPtr->Read<uint32>();
		auto idList = LoadSQLXListFromINetStream<uint32>(*pckPtr);
		AutoFeedbackLarge(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto sqlFormat = CreateSQL4SelectEntity<InstMailAttachment>(
			"mailReceiver=%u AND !isGetAttachment AND mailID IN (%s)");
		auto rst = connPtr->QueryFormat(
			sqlFormat.c_str(), playerId, idList.c_str());
		if (!rst) {
			error = DBPErrorQueryMysqlFailed;
			return;
		}
		while (rst.NextRow()) {
			auto row = rst.Fetch();
			auto instInfo = LoadEntityFromMysqlRow<InstMailAttachment>(row);
			SaveToINetStream(instInfo, buffer);
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(GetPlayerMailAttachment));
	return SessionHandleCapture;
}

int GameSessionHandler::HandlePlayerGetMailAttachment(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto PlayerGetMailAttachment = [GSRPCAsyncTaskArgs]() {
		auto playerId = pckPtr->Read<uint32>();
		auto idList = LoadSQLXListFromINetStream<uint32>(*pckPtr);
		AutoFeedback(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto sqlFormat = "UPDATE `%s` SET isGetAttachment=1 "
			"WHERE mailReceiver=%u AND mailID IN (%s)";
		auto rst = connPtr->ExecuteFormat(sqlFormat,
			GetTableName<inst_mail>(), playerId, idList.c_str());
		if (!rst.second) {
			error = DBPErrorUpdateMysqlFailed;
			return;
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(PlayerGetMailAttachment));
	return SessionHandleCapture;
}

int GameSessionHandler::HandlePlayerViewMailDetail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto PlayerViewMailDetail = [GSRPCAsyncTaskArgs]() {
		auto playerId = pckPtr->Read<uint32>();
		auto idList = LoadSQLXListFromINetStream<uint32>(*pckPtr);
		AutoFeedback(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto sqlFormat = "UPDATE `%s` SET isViewDetail=1 "
			"WHERE mailReceiver=%u AND mailID IN (%s)";
		auto rst = connPtr->ExecuteFormat(sqlFormat,
			GetTableName<inst_mail>(), playerId, idList.c_str());
		if (!rst.second) {
			error = DBPErrorUpdateMysqlFailed;
			return;
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(PlayerViewMailDetail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandlePlayerWriteMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto PlayerWriteMail = [GSRPCAsyncTaskArgs]() {
		inst_mail instInfo;
		LoadFromINetStream(instInfo, *pckPtr);
		AutoFeedback(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto sqlStr = CreateSQL4InsertEntity(instInfo);
		auto rst = connPtr->Execute(sqlStr.c_str());
		if (!rst.second) {
			error = DBPErrorInsertMysqlFailed;
			return;
		}
		buffer << instInfo.mailReceiver;
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(PlayerWriteMail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandlePlayerDeleteMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto PlayerDeleteMail = [GSRPCAsyncTaskArgs]() {
		auto playerId = pckPtr->Read<uint32>();
		auto idList = LoadSQLXListFromINetStream<uint32>(*pckPtr);
		AutoFeedback(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		auto sqlFormat = "DELETE FROM `%s` "
			"WHERE mailReceiver=%u AND mailID IN (%s)";
		auto rst = connPtr->ExecuteFormat(sqlFormat,
			GetTableName<inst_mail>(), playerId, idList.c_str());
		if (!rst.second) {
			error = DBPErrorDeleteMysqlFailed;
			return;
		}
	};
	sAsyncTaskMgr.AddTask(CreateAsyncTask(PlayerDeleteMail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleSystemMultiSameMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto SystemMultiSameMail = [GSRPCAsyncTaskArgs]() {
		auto mailCount = pckPtr->Read<uint32>();
		std::vector<uint32> receiverUids;
		receiverUids.reserve(mailCount);
		for (uint32 i = 0; i < mailCount; ++i) {
			receiverUids.push_back(pckPtr->Read<uint32>());
		}
		inst_mail instInfo;
		LoadFromINetStream(instInfo, *pckPtr);
		AutoFeedbackLarge(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		std::ostringstream sqlStream;
		for (uint32 i = 0; i < mailCount; ++i) {
			instInfo.mailReceiver = receiverUids[i];
			buffer << instInfo.mailReceiver;
			if (sqlStream.tellp() == std::ios::pos_type(0)) {
				sqlStream << CreateSQL4InsertEntity(instInfo);
			} else {
				sqlStream << CreateSQL4InsertEntityValues(instInfo);
			}
			if (sqlStream.tellp() > SQL_INSERT_MAIL_LENGTH_LIMIT || i + 1 >= mailCount) {
				if (connPtr->Execute(sqlStream.str().c_str()).second) {
					GameSession::Feedback(gsPtr, buffer, info.sn, RPCErrorNone, false);
				}
				buffer.Clear(), sqlStream.str("");
			}
		}
	};
	sDeferAsyncTaskMgr.AddTask(CreateAsyncTask(SystemMultiSameMail));
	return SessionHandleCapture;
}

int GameSessionHandler::HandleSystemMultiDifferentMail(GameSession *pSession, INetPacket &pck, const RPCSession::RequestMetaInfo &info)
{
	auto SystemMultiDifferentMail = [GSRPCAsyncTaskArgs]() {
		auto mailCount = pckPtr->Read<uint32>();
		std::vector<inst_mail> instInfos;
		instInfos.reserve(mailCount);
		for (uint32 i = 0; i < mailCount; ++i) {
			instInfos.push_back({});
			LoadFromINetStream(instInfos.back(), *pckPtr);
		}
		AutoFeedbackLarge(buffer, error)
		auto connPtr = sMysqlDatabasePool.GetCharDB()->GetConnAutoPtr();
		if (!connPtr) {
			error = DBPErrorConnectMysqlFailed;
			return;
		}
		std::ostringstream sqlStream;
		for (uint32 i = 0; i < mailCount; ++i) {
			buffer << instInfos[i].mailReceiver;
			if (sqlStream.tellp() == std::ios::pos_type(0)) {
				sqlStream << CreateSQL4InsertEntity(instInfos[i]);
			} else {
				sqlStream << CreateSQL4InsertEntityValues(instInfos[i]);
			}
			if (sqlStream.tellp() > SQL_INSERT_MAIL_LENGTH_LIMIT || i + 1 >= mailCount) {
				if (connPtr->Execute(sqlStream.str().c_str()).second) {
					GameSession::Feedback(gsPtr, buffer, info.sn, RPCErrorNone, false);
				}
				buffer.Clear(), sqlStream.str("");
			}
		}
	};
	sDeferAsyncTaskMgr.AddTask(CreateAsyncTask(SystemMultiDifferentMail));
	return SessionHandleCapture;
}
